﻿////////////////////////////////////////////////////////////////////////////////////////////////////
// file:	Test\TestReadCommitted.cs
//
// summary:	隔离级别读已提交及更低，会出现“不可重复读”现象
//                     A事务读取了两次数据，在这两次的读取过程中B事务修改了数据，A事务的这两次读取出来的数据不一样。
////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Linq;
using System.Threading;
using System.Transactions;
using Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using TestTransaction.Domain;

namespace TestTransaction.Test
{
    public class TestReadCommitted : TestBase
    {
        private AutoResetEvent _toWriteEvent = new AutoResetEvent(false);
        private AutoResetEvent _toReadEvent = new AutoResetEvent(false);

        [Test]
        public void ReadCommitted()
        {
            ThreadPool.QueueUserWorkItem(data => {
                Read();  //启动线程读
            });
            ThreadPool.QueueUserWorkItem(data => {
                Write();  //启动线程写
            });

            Thread.Sleep(5000);

            using (var context = _autofacServiceProvider.GetService<OpenAuthDBContext>())
            {
                var user = context.Users.SingleOrDefault(u => u.Account == "admin");
                Console.WriteLine($"最终用户状态:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
            }

        }

        private void Read()
        {
            using (var transactionScope = new TransactionScope(TransactionScopeOption.Required,
                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
            {
                using (var context = _autofacServiceProvider.GetService<OpenAuthDBContext>())
                {
                    var user = context.Users.SingleOrDefault(u => u.Account == "admin");
                    Console.WriteLine($"事务B：第一次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
                }

                _toWriteEvent.Set();  //模拟多线程切换，这时切换到写线程，复现不可重复读
                _toReadEvent.WaitOne();

                using (var context = _autofacServiceProvider.GetService<OpenAuthDBContext>())
                {
                    var user = context.Users.SingleOrDefault(u => u.Account == "admin");
                    Console.WriteLine($"事务B：第二次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
                }
            }

        }

        private void Write()
        {
            _toWriteEvent.WaitOne();

            using (var scope = new TransactionScope(TransactionScopeOption.Required,
                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
            {
                User user = null;
                using (var context = _autofacServiceProvider.GetService<OpenAuthDBContext>())
                {
                    user = context.Users.SingleOrDefault(u => u.Account == "admin");
                    Console.WriteLine($"事务A：读取为【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
                    user.Status = 1 - user.Status;
                    context.SaveChanges();
                }
                scope.Complete();
                Console.WriteLine($"事务A：已被更改为【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
                _toReadEvent.Set();
            }
        }
    }
}
